package com.twinkle.user.biz.runner;

import cn.hutool.core.collection.CollUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.twinkle.framework.common.utils.JsonUtils;
import com.twinkle.user.biz.constant.RedisKeyConstants;
import com.twinkle.user.biz.mapper.PermissionMapper;
import com.twinkle.user.biz.mapper.RoleMapper;
import com.twinkle.user.biz.mapper.RolePermissionRelMapper;
import com.twinkle.user.biz.model.entity.Permission;
import com.twinkle.user.biz.model.entity.Role;
import com.twinkle.user.biz.model.entity.RolePermissionRel;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
@Slf4j
public class PushRolePermissions2RedisRunner implements ApplicationRunner {
    @Resource
    private RoleMapper roleMapper;
    @Resource
    private RolePermissionRelMapper rolePermissionRelMapper;
    @Resource
    private PermissionMapper permissionMapper;
    @Resource
    private RedisTemplate redisTemplate;

    // 权限同步标记 Key
    private static final String PUSH_PERMISSION_FLAG = "push.permission.flag";

    @Override
    public void run(ApplicationArguments args) {
            log.info("==> 服务启动，开始同步角色权限数据到 Redis 中...");
            try{
                boolean canPushed = redisTemplate.opsForValue().setIfAbsent(PUSH_PERMISSION_FLAG, "1", 1, TimeUnit.DAYS);
                // 如果无法同步权限数据
                if (!canPushed) {
                    log.warn("==> 角色权限数据已经同步至 Redis 中，不再同步...");
                    return;
                }
                //查询所有角色
                List<Role> roles = roleMapper.selectEnabledList();

                if (CollUtil.isNotEmpty(roles)) {
                    List<Long> roleIds = roles.stream().map(Role::getId).toList();
                    //查询所有角色对应的权限
                    List<RolePermissionRel>  rolePermissionRels = rolePermissionRelMapper.selectByRoleIds(roleIds);
                    //以角色id为key，每个角色可以对应多个权限
                    Map<Long, List<Long>> rolePermissionRelsMap = rolePermissionRels.stream().collect(
                            Collectors.groupingBy(RolePermissionRel::getRoleId,
                                                  Collectors.mapping(RolePermissionRel::getPermissionId, Collectors.toList()))
                    );
                //获取所有被启用的权限
                List<Permission> permissions = permissionMapper.selectEnabledList();
                Map<Long, Permission> permissionsMap = permissions.stream().collect(
                            Collectors.toMap(Permission::getId, p -> p)
                );

                //角色id与权限的关系
                Map<String, List<String>> rolePermissionMap = Maps.newHashMap();
                roles.forEach(role -> {
                    Long roleId = role.getId();
                    String roleKey = role.getRoleKey();
                    List<Long> permissionIds = rolePermissionRelsMap.get(roleId);
                    if (CollUtil.isNotEmpty(permissionIds)) {
                        List<String> permissionList = Lists.newArrayList();
                        permissionIds.forEach(permissionId -> {
                            Permission permission = permissionsMap.get(permissionId);
                            if (Objects.nonNull(permission)) permissionList.add(permission.getPermissionKey());
                        });
                        rolePermissionMap.put(roleKey, permissionList);
                    }
                });

                //同步至redis中
                rolePermissionMap.forEach((roleKey, permissionIdList) -> {
                   String key = RedisKeyConstants.buildRolePermissionKey(roleKey);
                   redisTemplate.opsForValue().set(key, JsonUtils.toJsonString(permissionIdList));
                });
                }
                log.info("==> 服务启动，成功同步角色权限数据到 Redis 中...");
            }catch (Exception e){
                log.info("==> 同步角色权限数据到 Redis 中失败...", e);
            }
    }
}
